home *** CD-ROM | disk | FTP | other *** search
- /* 02.04.1989 amn (latest edit) */
-
- /* PDEF1.c - printer driver for Macintosh and HP DeskJet, spooling. */
-
- /* Compiles into 'PDEF' resource, id 1, name ''. */
- /* We cannot use any global variables in this code. However, the globals of the */
- /* low-level driver (XPrint) are available thru the handle 'dCtlStorage' in the */
- /* driver's device control entry. */
-
- /* This resource is placed into the printer resource file */
- /* 'HP DeskJet', type 'PRER', creator 's89^' as */
- /* 'PDEF' 1 '' by 'PRER_Builder' utility program. This utility adds a small jump */
- /* table in front of the code produced by LightspeedC. */
-
- /* These procedures handle spool file recording... */
-
- /* Authors: Ari Mujunen (amn@hutcs.hut.fi) and Olli Arnberg (oar@hutcs.hut.fi). */
- /* Copyright Ari Mujunen, Olli Arnberg 1989. */
- /* You may redistribute the driver (=printer resource file, source files, */
- /* documentation file(s), and the file 'Copyright and Source Offer') */
- /* only _non-commercially_ and _in its entirety_. */
- /* See the file 'Copyright and Source Offer' and/or documentation for details. */
- /* Acknowledgements: Special thanks to Mr. Earle R. Horton for his 'Daisy' */
- /* daisywheel printer driver and its source code published in 'MacTutor', Nov-Dec 1987. */
- /* This driver served as a basis and inspiration for our work. It also */
- /* proofed that a Macintosh printer driver can be done despite the lack of */
- /* documentation from Apple. */
-
- /* Change history: */
- /* Version When Who Why */
- /* 2.0 09.12.1988 amn Original rewrite. */
- /* 11.12.1988 amn Cleaning up. */
- /* 15.12.1988 amn Differentiate between our idle proc */
- /* that of application. */
- /* 25.02.1989 amn Accidentally locked PDEF 0 instead of 1. */
- /* 10.03.1989 amn Saving the length of each string drawn */
- /* as a picture comment. */
- /* 25.03.1989 amn Removing global file refNums. */
- /* 28.03.1989 amn Settable printer origin. */
- /* 30.03.1989 amn,oar Option-OK causes spool file to be named and */
- /* saved for subsequent batch printing. */
- /* 31.03.1989 amn More permanent spool file. */
- /* 2.1 02.04.1989 amn,oar Released version. */
-
-
- #include "common_mac_includes.h"
-
- /* Mac OS includes specific to this module: */
- #include <ToolboxUtil.h>
- #include <StdFilePkg.h>
-
-
- #include "prglobals.h"
-
- #include "procedures_for_PDEF1.c"
- #include "procedures_for_PDEF5.c" /* idle proc is here */
-
-
-
- /* Function prototypes. */
-
- /* Procedure dispatcher. */
- void main(int);
-
- /* Initializes a specialized grafPort for printing. Opens spool file. */
- pascal TPPrPort myPrOpenDoc(THPrint, TPPrPort, Ptr);
-
- /* Disposes the printing grafPort. Updates spool file header on disk. Closes spool file. */
- pascal void myPrCloseDoc(TPPrPort);
-
- /* Reinitializes the printing grafPort. */
- pascal void myPrOpenPage(TPPrPort, TPRect);
-
- /* Updates spool file header in memory. */
- pascal void myPrClosePage(TPPrPort);
-
-
- /* Replaces the standard QuickDraw low-level procedure for drawing text. */
- pascal void myStdText(int, Ptr, Point, Point);
-
- /* Used to call standard QD text-drawing procedure. We use prototypes... */
- pascal void CallPascal(int, Ptr, Point, Point, Ptr);
-
- pascal void putPICTData(Ptr, int);
-
- void copystr(StringPtr, StringPtr);
-
-
-
- /* Function definitions. */
-
-
- void
- main(routineSelector)
- int routineSelector;
- {
- /* The jump table code inserted before our code resource by 'PRER_Builder' */
- /* utility program pushes a word onto stack indicating which routine is called. */
- /* We pop it off the stack and select an appropriate routine. */
-
- switch(routineSelector) {
- case 0:
- asm {
- unlk a6 ;; LSC generates an 'link' instruction to access parameters
- move.l (a7)+, d0 ;; pop return address to our jump table code (discarded)
- move.w (a7)+, d0 ;; pop argument 'routineSelector'
- jmp myPrOpenDoc
- }
- case 1:
- asm {
- unlk a6
- move.l (a7)+, d0
- move.w (a7)+, d0
- jmp myPrCloseDoc
- }
- case 2:
- asm {
- unlk a6
- move.l (a7)+, d0
- move.w (a7)+, d0
- jmp myPrOpenPage
- }
- case 3:
- asm {
- unlk a6
- move.l (a7)+, d0
- move.w (a7)+, d0
- jmp myPrClosePage
- }
- } /* switch */
-
- /* We should not arrive here; Printing Manager has called a non-existent routine. */
- SysError(5); /* check bounds trap ??? */
- } /* main */
-
-
- /*
- This function is supposed to return a pointer to a specialized GrafPort (a TPrPort) customized
- for printing. Due to the paucity of documentation on how to go about this, I do not know whether
- I am going about this in exactly the right way, but I sure hope so. I set portBits.bounds for
- the port to the empty Rect (0,0,0,0).
- Other tasks: save a copy of the user print record in the printer resource file
- to remember user's last choices in job dialog (resolution etc.).
- */
-
-
- #define DEBUG_OPEN_PRINT_RECORD
-
- pascal TPPrPort
- myPrOpenDoc(hPrint, pPrPort, pIOBuf)
- THPrint hPrint;
- TPPrPort pPrPort;
- Ptr pIOBuf;
- {
- TPPrPort thisPort;
- ptXprintGlobals xPrintGlobals;
- Boolean spoolFileExistedBeforeThisOpenDoc;
- OSErr retCode;
-
- { /* Lock this code resource in memory (Printing Manager should do this, but...). */
- Handle us;
-
- us = (Handle)GetResource('PDEF', 1);
- if (us == nil) {
- PrintErr = ResError();
- return(nil);
- }
- HLock(us);
- }
-
- if (pPrPort == nil) { /* printing GrafPort NOT preallocated */
- if ((thisPort = (TPPrPort)NewPtr((long)sizeof(TPrPort))) == nil) {
- PrintErr = iMemFullErr;
- return(nil);
- }
- thisPort->fOurPtr = TRUE;
- }
- else { /* printing GrafPort already preallocated */
- /* We cannot check the size of the port given to us, since it can be */
- /* in application globals, not necessarily a block reserved with NewPtr. */
- thisPort = pPrPort;
- thisPort->fOurPtr = FALSE;
- }
-
- xPrintGlobals = GET_XPRINT_GLOBALS;
-
- xPrintGlobals->hSpoolFileHeader = nil;
-
-
- /* Initialize GrafPort */
- OpenPort(&thisPort->gPort); /* could this cause a Memory Manager error ??? */
- setRectsAndRgns(thisPort, hPrint);
-
- /* Determine the name of the spool file. */
- if (!xPrintGlobals->spoolFileAlreadyNamed) {
- StringHandle defaultSpoolFileNameHandle;
- StringPtr defaultSpoolFileNamePtr;
- Str255 defaultSpoolFileName;
-
- /* Page range etc. are available in global print record. */
- xPrintGlobals->printRecord = **hPrint;
-
- /* Default name from either print record or from resource. */
- if (xPrintGlobals->printRecord.prJob.pFileName != nil) {
- defaultSpoolFileNamePtr = xPrintGlobals->printRecord.prJob.pFileName;
- /* We assume volume number is ok. */
- }
- else {
- defaultSpoolFileNameHandle = GetString(RESID_OWNED_BY_PDEF + 1);
- if (defaultSpoolFileNameHandle == nil)
- copystr((StringPtr)"\pPrint File", defaultSpoolFileName);
- else {
- LoadResource(defaultSpoolFileNameHandle);
- copystr(*defaultSpoolFileNameHandle, defaultSpoolFileName);
- HPurge(defaultSpoolFileNameHandle);
- }
- defaultSpoolFileNamePtr = defaultSpoolFileName;
- xPrintGlobals->printRecord.prJob.iFileVol = 0; /* default volume ??? */
- }
-
- if (xPrintGlobals->spoolFileIsNamedAndPermanent) {
- Point where;
- SFReply reply;
- StringHandle hPrompt;
- int saveVRefNum;
- Str255 dummySaveVolName;
-
- /* Set default volume for StdFile dialog starting point. */
- if ((retCode = GetVol(&dummySaveVolName, &saveVRefNum)) != noErr)
- goto cleanUp;
- if ((retCode = SetVol(nil, xPrintGlobals->printRecord.prJob.iFileVol)) != noErr)
- goto cleanUp;
-
- where.v = where.h = 100;
- /* Get prompt; (RESID+2 is used by SuperSpool...) */
- if ((hPrompt = GetString(RESID_OWNED_BY_PDEF + 3)) != nil) {
- HLock(hPrompt);
- SFPutFile(
- where,
- *hPrompt,
- defaultSpoolFileNamePtr, /* original name */
- nil,
- &reply
- );
- HUnlock(hPrompt);
- HPurge(hPrompt);
- }
- else {
- SFPutFile(
- where,
- (StringPtr)"\pSave spool file as:",
- defaultSpoolFileName, /* original name */
- nil,
- &reply
- );
- }
-
- if ((retCode = SetVol(nil, saveVRefNum)) != noErr)
- goto cleanUp; /* default volume is probably messed up... */
-
- if (reply.good) {
- copystr(reply.fName, defaultSpoolFileName);
- defaultSpoolFileNamePtr = defaultSpoolFileName;
- xPrintGlobals->printRecord.prJob.iFileVol = reply.vRefNum;
- xPrintGlobals->spoolFileAlreadyNamed = TRUE;
- }
- else { /* User clicked Cancel, resume normal printing. */
- xPrintGlobals->spoolFileIsNamedAndPermanent = FALSE;
- xPrintGlobals->spoolFileAlreadyNamed = FALSE;
- }
- } /* if permanent spool file */
-
- /* Now we copy the spool file name from either application memroy */
- /* or our temp variable to the global spoolFileName; global */
- /* print record reJob.pFileName field points to this variable. */
- {
- copystr(defaultSpoolFileNamePtr, xPrintGlobals->spoolFileName);
- xPrintGlobals->printRecord.prJob.pFileName = xPrintGlobals->spoolFileName;
- /* xPrintGlobals->printRecord.prJob.iFileVol is already ok */
- xPrintGlobals->printRecord.prJob.bFileVers = 0;
- }
- } /* end if has to determine spool file name */
-
- /* Open spool file, set its file type ('PFIL''^89s'). */
- /* '^89s' is the signature of the batch printing utility, BatchPrint. */
- retCode = Create(
- xPrintGlobals->printRecord.prJob.pFileName,
- xPrintGlobals->printRecord.prJob.iFileVol,
- '^89s',
- 'PFIL'
- );
- spoolFileExistedBeforeThisOpenDoc = (retCode == dupFNErr);
- if (!((retCode == noErr) || (retCode == dupFNErr)))
- goto cleanUp;
-
- retCode = FSOpen(
- xPrintGlobals->printRecord.prJob.pFileName,
- xPrintGlobals->printRecord.prJob.iFileVol,
- &(xPrintGlobals->spoolFileRefNum)
- );
- if (retCode != noErr)
- goto deleteAndCleanUp;
-
- /* Allocate memory buffer for page directory. */
- if ((xPrintGlobals->hSpoolFileHeader = (THPfHeader)NewHandle((long)sizeof(TPfHeader))) == nil) {
- retCode = iMemFullErr;
- goto closeAndDeleteAndCleanUp;
- }
-
- /* Add pages to existing spool file _or_ initialize new spool file. */
- if (spoolFileExistedBeforeThisOpenDoc) {
- long count;
-
- count = sizeof(TPfHeader);
- HLock(xPrintGlobals->hSpoolFileHeader);
- retCode = FSRead(xPrintGlobals->spoolFileRefNum, &count, *(xPrintGlobals->hSpoolFileHeader));
- HUnlock(xPrintGlobals->hSpoolFileHeader);
- if (retCode != noErr)
- goto disposAndCloseAndDeleteAndCleanUp;
-
- /* Position file to logical end to receive additional pages. */
- if ((retCode = SetFPos(xPrintGlobals->spoolFileRefNum, fsFromLEOF, 0L)) != noErr)
- goto disposAndCloseAndDeleteAndCleanUp;
- }
- else { /* Prepend print spool file header for PFIL file format: */
- long count;
- int i;
-
- /* Initialize header to current print record and all zeroes. */
- for (i=0;i<sizeof(TPfHeader);i++)
- ((unsigned char *)(*xPrintGlobals->hSpoolFileHeader))[i] = (unsigned char)0;
- (*xPrintGlobals->hSpoolFileHeader)->print = **hPrint;
- (*xPrintGlobals->hSpoolFileHeader)->print.prJob.pIdleProc = nil;
- (*xPrintGlobals->hSpoolFileHeader)->print.prJob.pFileName = nil;
- (*xPrintGlobals->hSpoolFileHeader)->print.prJob.iFileVol = 0;
-
- count = sizeof(TPfHeader);
- HLock(xPrintGlobals->hSpoolFileHeader);
- retCode = FSWrite(xPrintGlobals->spoolFileRefNum, &count, *(xPrintGlobals->hSpoolFileHeader));
- HUnlock(xPrintGlobals->hSpoolFileHeader);
- if (retCode != noErr)
- goto disposAndCloseAndDeleteAndCleanUp;
-
- #define NUMBER_OF_THIS_PDEF 1
- #include "save_last_used.c"
- #undef NUMBER_OF_THIS_PDEF
-
- /* If application has not requested an own idle procedure, we set our default one. */
- if (xPrintGlobals->printRecord.prJob.pIdleProc == nil) {
- xPrintGlobals->printRecord.prJob.pIdleProc = (ProcPtr)checkForCommandPeriod;
- xPrintGlobals->applicationOwnsIdleProc = FALSE;
- }
- else
- xPrintGlobals->applicationOwnsIdleProc = TRUE;
- /* For example WriteNow seems to rely on 'pIdleProc' being non-nil. */
- /* That is why we must set it -- although we don't call idle proc */
- /* ourselves, application might be that friendly and do it for us. */
-
- } /* else spool file is new */
-
- /* High-level printing code always counts pages from 1 (IM II-156). */
- xPrintGlobals->pageCounter = 1;
-
- #ifdef DEBUG_OPEN_PRINT_RECORD
- /* Print out the contents of the print record to find out what applications store into it. */
- #endif DEBUG_OPEN_PRINT_RECORD
-
- retCode = noErr;
- cleanUp:
- PrintErr = retCode;
- return(thisPort);
- /* An application conforming to IM II-155 will call 'PrCloseDoc' regardless the value of */
- /* 'PrError'. It is 'PrCloseDoc's duty to free 'thisPort'. */
-
- disposAndCloseAndDeleteAndCleanUp:
- DisposHandle(xPrintGlobals->hSpoolFileHeader);
- xPrintGlobals->hSpoolFileHeader = nil;
- closeAndDeleteAndCleanUp:
- (void)FSClose(xPrintGlobals->spoolFileRefNum);
- deleteAndCleanUp:
- (void)FSDelete(
- xPrintGlobals->printRecord.prJob.pFileName,
- xPrintGlobals->printRecord.prJob.iFileVol
- );
- goto cleanUp;
- } /* myOpenDoc */
-
-
- pascal void
- myPrCloseDoc(pPrPort)
- TPPrPort pPrPort;
- {
- ptXprintGlobals xPrintGlobals;
- OSErr retCode;
-
- if (pPrPort == nil) /* if opening did not succeed, we do nothing */
- return;
-
- xPrintGlobals = GET_XPRINT_GLOBALS;
-
- if (PrintErr != noErr) { /* something has gone wrong, we must clean up our port */
- retCode = PrintErr;
- goto disposAndCloseAndDeleteAndCleanUp;
- }
-
- /* Now we are at the end of last page spooled, mark logical end of file. */
- {
- long filePosition;
-
- if ((retCode = GetFPos(xPrintGlobals->spoolFileRefNum, &filePosition)) != noErr)
- goto disposAndCloseAndDeleteAndCleanUp;
- if ((retCode = SetEOF(xPrintGlobals->spoolFileRefNum, filePosition)) != noErr)
- goto disposAndCloseAndDeleteAndCleanUp;
- }
-
- /* Update print spool file header (page directory) for PFIL file format: */
- {
- long count;
-
- retCode = SetFPos(xPrintGlobals->spoolFileRefNum, fsFromStart, 0L);
- if (retCode != noErr)
- goto disposAndCloseAndDeleteAndCleanUp;
-
- count = sizeof(TPfHeader);
- HLock(xPrintGlobals->hSpoolFileHeader);
- retCode = FSWrite(xPrintGlobals->spoolFileRefNum, &count, *xPrintGlobals->hSpoolFileHeader);
- HUnlock(xPrintGlobals->hSpoolFileHeader);
- if (retCode != noErr)
- goto disposAndCloseAndDeleteAndCleanUp;
-
- DisposHandle(xPrintGlobals->hSpoolFileHeader);
- }
-
- retCode = FSClose(xPrintGlobals->spoolFileRefNum);
- if (retCode != noErr)
- goto deleteAndCleanUp;
-
- retCode = noErr;
- cleanUp:
- PrintErr = retCode;
- ClosePort(pPrPort);
- if (pPrPort->fOurPtr) /* if we allocated the port, we have to deallocate it too */
- DisposPtr(pPrPort);
- return;
-
- disposAndCloseAndDeleteAndCleanUp:
- if (xPrintGlobals->hSpoolFileHeader == nil)
- goto cleanUp;
- DisposHandle(xPrintGlobals->hSpoolFileHeader);
- xPrintGlobals->hSpoolFileHeader = nil;
- closeAndDeleteAndCleanUp:
- (void)FSClose(xPrintGlobals->spoolFileRefNum);
- deleteAndCleanUp:
- (void)FSDelete(
- xPrintGlobals->printRecord.prJob.pFileName,
- xPrintGlobals->printRecord.prJob.iFileVol
- );
- goto cleanUp;
- } /* myCloseDoc */
-
-
- pascal void
- myPrOpenPage(pPrPort, pPageFrame)
- TPPrPort pPrPort;
- TPRect pPageFrame;
- {
- ptXprintGlobals xPrintGlobals;
- OSErr retCode;
-
- if (pPrPort == nil)
- /* There is not much we can do; */
- /* the application will 'print' into a very odd GrafPort. */
- return;
-
- /* If (PrintErr != noErr), we assume we have a port to satisfy the application, */
- /* but something has gone awry with the spool file. */
-
-
- xPrintGlobals = GET_XPRINT_GLOBALS;
-
- /* Port is returned to initial state (as initialized in OpenDoc). */
- InitPort(pPrPort);
- {
- TPPrint pPrint;
-
- pPrint = &xPrintGlobals->printRecord;
- setRectsAndRgns(pPrPort, &pPrint);
- }
-
- xPrintGlobals->hPagePicture = nil;
-
- /* For pages not to be printed, return a GrafPort which does not record a QD-picture. */
- if ((xPrintGlobals->pageCounter < xPrintGlobals->printRecord.prJob.iFstPage)
- || (xPrintGlobals->pageCounter > xPrintGlobals->printRecord.prJob.iLstPage)
- || (PrintErr != noErr)) {
- return;
- }
-
- /* Open QD-picture using either page rectangle */
- /* or application-specified rectangle as picture frame. */
- {
- Rect pictureFrame;
-
- if (pPageFrame != nil)
- pictureFrame = *pPageFrame; /* application wants to scale */
- else
- pictureFrame = xPrintGlobals->printRecord.prInfo.rPage;
-
- xPrintGlobals->hPagePicture = OpenPicture(&pictureFrame);
- if (xPrintGlobals->hPagePicture == nil) {
- PrintErr = iMemFullErr;
- return;
- }
- }
-
- /* Update page directory in memory (page picture starting offset). */
- {
- long position;
- int i;
-
- retCode = GetFPos(xPrintGlobals->spoolFileRefNum, &position);
- if (retCode != noErr) {
- PrintErr = retCode;
- return;
- }
-
- /* Store start offset in spool file according to the header page number. */
- i = (++((*xPrintGlobals->hSpoolFileHeader)->pfPgDir.iPages));
- (*xPrintGlobals->hSpoolFileHeader)->pfPgDir.lPgPos[i]
- = position;
- }
-
- /* Write the beginning of page picture data to spool file. */
- {
- long count;
-
- count = (long)sizeof(Picture);
- HLock(xPrintGlobals->hPagePicture);
- retCode = FSWrite(xPrintGlobals->spoolFileRefNum, &count, *xPrintGlobals->hPagePicture);
- HUnlock(xPrintGlobals->hPagePicture);
- if (retCode != noErr) {
- PrintErr = retCode;
- return;
- }
- }
-
- /* Set our own procedure for storing picture data directly to spool file. */
- SetStdProcs(&pPrPort->gProcs);
- pPrPort->gPort.grafProcs = &pPrPort->gProcs;
- pPrPort->gProcs.textProc = (QDPtr)myStdText;
- pPrPort->gProcs.putPicProc = (QDPtr)putPICTData;
-
- ClipRect(&pPrPort->gPort.portRect); /* ImageWriter records this, so do we. */
-
- /* Finders 4.1 & 6.0 seem to rely on printing GrafPort's font being application font. */
- /* Turbo Pascal 1.1 seems not to like this, though. */
- TextFont(applFont);
- } /* myPrOpenPage */
-
-
- pascal void
- myPrClosePage(pPrPort)
- TPPrPort pPrPort;
- {
- ptXprintGlobals xPrintGlobals;
-
- if (pPrPort == nil) /* Guard against stupid applications */
- return;
-
- xPrintGlobals = GET_XPRINT_GLOBALS;
-
- /* If we were recording QD-picture to disk, we close it. */
- if (xPrintGlobals->hPagePicture != nil) {
- ClosePicture(); /* close picture to get the end_of_picture -mark to disk */
- pPrPort->gPort.grafProcs = nil; /* no more special drawing procs */
- KillPicture(xPrintGlobals->hPagePicture);
- xPrintGlobals->hPagePicture = nil;
- }
-
- /* If the capacity of spool file header is exceeded, cancel printing. */
- if (((*xPrintGlobals->hSpoolFileHeader)->pfPgDir.iPages) >= iPfMaxPgs)
- PrintErr = iPrAbort;
-
- xPrintGlobals->pageCounter++;
- } /* myClosePage */
-
-
- pascal void
- myStdText(byteCount, textBuffer, numer, denom)
- int byteCount;
- Ptr textBuffer;
- Point numer, denom;
- {
- htApplicationComment lengthComment;
- QDProcs stdProcs;
-
- if ((lengthComment = (htApplicationComment)NewHandle(sizeof(tApplicationComment))) != nil) {
- (*lengthComment)->applicationSignature = 's89^';
- (*lengthComment)->localKind = 0;
- (*lengthComment)->originalLengthOfNextTextInPixels =
- TextWidth(textBuffer, 0, byteCount);
- /* Hmm, what about 'numer' and 'denom' ? */
- /* Seems to work ok though, even when QD has to scale */
- /* the font when recording PICT. */
-
- PicComment(100, sizeof(tApplicationComment), lengthComment);
- DisposHandle(lengthComment);
- }
- /* Finally, draw text using standard procedure. */
- SetStdProcs(&stdProcs);
- CallPascal(byteCount, textBuffer, numer, denom, stdProcs.textProc);
- } /* myStdText */
-
-
- pascal void
- putPICTData(dataPtr, byteCount)
- Ptr dataPtr;
- int byteCount;
- {
- long count;
- ptXprintGlobals xPrintGlobals;
- OSErr retCode;
-
- if (PrintErr == noErr) {
- count = byteCount;
- xPrintGlobals = GET_XPRINT_GLOBALS;
- retCode = FSWrite(xPrintGlobals->spoolFileRefNum, &count, dataPtr);
- if (retCode != noErr)
- PrintErr = retCode;
- }
- } /* putPICTData */
-
-
- void
- copystr(src, dst)
- StringPtr src, dst;
- /* This is for copying PASCAL strings (simple) */
- {
- asm {
- clr.l d0
- move.l src,a0
- move.l dst,a1
- move.b (a0),d0
- loop:
- move.b (a0)+,(a1)+
- dbra d0,@loop
- }
- }
-